#include <lcir/ADT/Range.h>
#include <lcir/IR/Argument.h>
#include <lcir/IR/Expression.h>
#include <lcir/IR/Lambda.h>
#include <lcir/IR/LambdaProto.h>
#include <lcir/IR/LambdaType.h>
#include <lcir/IR/Prototype.h>
#include <lcir/IR/Value.h>
#include <lcir/IR/ValuePrinter.h>

#include <cstddef>
#include <iterator>
#include <memory>
#include <stdexcept>
#include <vector>

#include "lcir/IR/ExprTemps.h"
#include "lcir/IR/LambdaBase.h"
namespace lcir {
Lambda::Lambda(std::unique_ptr<LambdaProto> proto) : ValueCRTP(ValueKind::VK_Lambda) {
  Value::proto = std::move(proto);
  Value::proto->setValue(this);
  auto *ty = Value::proto->getType()->dyn_cast<LambdaType>();
  std::vector<Type *> argTys;
  for (auto *argTy : ty->getParamTypes()) {
    argTys.push_back(argTy);
  }
  initArgs(argTys);
}
Lambda::Lambda() : Lambda(std::make_unique<LambdaProto>(LambdaType::get())) {}
Lambda::Lambda(const std::vector<Type *> &paramTypes, Type *resultType)
    : Lambda(std::make_unique<LambdaProto>(LambdaType::get({paramTypes}, resultType))) {}

void Lambda::initArgs(const std::vector<Type *> &argTypes) {
  for (auto *argTy : argTypes) {
    arguments.pushBack(new Argument(std::make_unique<LambdaArgumentProto>(argTy), this));
  }
}
void Lambda::setProto(std::unique_ptr<LambdaProto> newProto) {
  if (Value::proto) Value::proto.reset();
  Value::proto = std::move(newProto);
}

LambdaProto *Lambda::getLambdaProto() const { return getProto()->dyn_cast<LambdaProto>(); }
LambdaType *Lambda::getLambdaType() const { return getType()->dyn_cast<LambdaType>(); }
std::vector<Argument *> Lambda::getArgs() const {
  std::vector<Argument *> ret;
  for (auto &arg : arguments) {
    ret.push_back(&arg);
  }
  return ret;
}

Argument *Lambda::getArg(size_t idx) const {
  if (idx >= arguments.size()) throw std::runtime_error("lambda argument index is out of range!");
  return &arguments[idx];
}

std::vector<Type *> Lambda::getParamTypes() const { return Value::proto->cast<LambdaProto>()->getParamTypes(); }

Type *Lambda::getResultType() const { return Value::proto->cast<LambdaProto>()->getResultType(); }

void Lambda::addArg(Type *ty) {
  arguments.pushBack(new Argument(std::make_unique<LambdaArgumentProto>(ty), this));
  auto paramTy = Value::proto->cast<LambdaProto>()->getParamTypes();
  paramTy.push_back(ty);
  auto newProto = std::make_unique<LambdaProto>(LambdaType::get(paramTy, getResultType()));
  setProto(std::move(newProto));
}

// void Lambda::addArgs(const std::vector<Type *> &typs) {}
// void Lambda::setArgs(const std::vector<Type *> &types) {
//   std::make_unique<LambdaProto>(LambdaType::get(types, getResultType()));
//   setProto(std::move(proto));
//   initArgs(types);
// }

size_t Lambda::getDepth() const {
  size_t depth{0};
  const Value *root = this;
  // 如果value不是root，找到root
  while (auto *parent = root->getDefExprParent()) {
    depth++;
    root = parent;
  }
  return depth;
}

std::vector<std::unique_ptr<Expression>> Lambda::removeFromLambda(Expression *begin, Expression *end) {
  std::vector<std::unique_ptr<Expression>> ret;
  for (auto *it = begin; it != end;) {
    auto *expr = it;
    it = it->getNext();
    auto up = expr->removeFromLambda();
     ret.push_back(std::move(up));
  }
  return ret;
}
Lambda* Lambda::makeNewLambda() {
  auto exprs = removeFromLambda(&*begin(), &*end()); 
  auto *newLambda = Lambda::get();
  for(auto& expr: exprs) {
    newLambda->pushBackExpr(std::move(expr));
  }
  return newLambda;
}


bool Lambda::isSealed() const {
  if(getExprs().empty()) return false;
  return getExprs().back()->hasTemp<Terminator>();
}


void Lambda::setDefExpr(Expression *expr) {
  if (defExpr)
    throw std::runtime_error("lambda defexpr cannot seted twice!");
  else
    defExpr = expr;
}
std::string Lambda::print() const {
  ValuePrinter printer(this);
  return printer.print(this);
}
}  // namespace lcir